🎓 Maestría en Inteligencia Artificial Aplicada¶

Institution Course Activity


🖼️ Visión Computacional para Imágenes y Video¶

👨‍🏫 Profesores¶

  • Profesor Titular: Dr. Gilberto Ochoa Ruiz
  • Profesor Asistente: MIP Ma. del Refugio Melendez Alfaro
  • Profesor Tutor: M. en C. Jose Angel Martinez Navarro

Actividad 9.4: Google Colab con algoritmo Otsu¶

📌 Detalles de la Actividad¶

  • Código: 9.4 Google Colab
  • Título: Algoritmo Otsu
  • Fecha de entrega: 📅 Nov 9, 2025 a las 23:59
  • Formato de entrega: ZIP
  • Modalidad: Equipo

👥 Team 13¶

🚀 Nuestro Equipo¶

Javier Augusto Rebull Saucedo¶

Javier Augusto Rebull Saucedo

Matrícula: A01795838
🎓 MNA Student

Juan Carlos Pérez Nava¶

Juan Carlos Pérez Nava

Matrícula: A01795941
🎓 MNA Student

Luis Gerardo Sánchez Salazar¶

Luis Gerardo Sánchez Salazar

Matrícula: A01232963
🎓 MNA Student

Oscar Enrique García García¶

Oscar Enrique García García

Matrícula: A01016093
🎓 MNA Student


Objetivo del Proyecto¶

El objetivo de esta sesión práctica es implementar el algoritmo de segmentación de Otsu, así como conocer sus fortalezas y debilidades


📚 Tabla de Contenidos¶

  1. Configuración Inicial e Importación de Librerías
  2. Umbralización única
  3. Umbralización múltiple

💻 Tecnologías Utilizadas¶

  • Python 3.8+ - Lenguaje de programación
  • Skimage - Para procesamiento de imágenes
  • NumPy - Computación numérica
  • Matplotlib / Seaborn - Visualización de datos
  • Google Colab - Entorno de ejecución
  • gdown - Descarga de datasets desde Google Drive

Importación de Librerías ¶

📦 Carga de Librerías y Configuración del Entorno¶

Este bloque inicial prepara el entorno para procesamiento y visualización de imágenes, así como para el manejo de archivos en diferentes formatos y la obtención de información del sistema.

🔍 Librerías Principales¶

  • matplotlib.pyplot
    Se utiliza para mostrar imágenes, histogramas y resultados de procesamiento visual.

  • numpy
    Manejo de arreglos y operaciones numéricas optimizadas, indispensables para el tratamiento de imágenes.

🖼️ Procesamiento de Imágenes y Umbralización¶

  • skimage.data
    Proporciona imágenes de ejemplo para pruebas rápidas.

  • threshold_otsu y threshold_multiotsu
    Métodos de umbralización automática.

    • Otsu calcula un umbral óptimo para separar la imagen en fondo y objeto.
    • Multi-Otsu extiende el método para segmentar en múltiples clases.
  • try_all_threshold
    Permite comparar visualmente diferentes métodos de umbralización.

  • imageio.v3 y PIL.Image
    Lectura y manipulación de imágenes en distintos formatos.

🖼️ Soporte para Formato HEIC/HEIF¶

  • pillow-heif
    Se instala y registra para permitir la carga de imágenes .heic y .heif de forma directa con PIL.

🛠️ Utilidades del Sistema¶

  • gdown
    Descarga archivos desde Google Drive.

  • os, sys, socket, platform, pathlib
    Proporcionan acceso al sistema operativo, entorno de ejecución y rutas.

  • datetime y ZoneInfo
    Se utilizan para imprimir una marca de tiempo personalizada con zona horaria.

✅ Mensaje Final¶

Se imprime:

  • Confirmación de que las librerías han sido cargadas.
  • La fecha y hora del sistema con zona horaria America/Mexico_City.
  • Información sobre país y región del entorno (si hay conexión a internet).
  • Versión de Python, ejecutable, plataforma, hostname, carpeta activa y usuario.

Esto ayuda a documentar el contexto de ejecución, lo cual es útil para reproducir resultados en distintos entornos.

In [ ]:
# ===============================================================
# Librerías para Visualización y Procesamiento Numérico
# ===============================================================
import matplotlib.pyplot as plt  # Biblioteca para generar gráficos y visualizar resultados de procesamiento
import numpy as np               # Biblioteca para operaciones numéricas y manejo de arrays
import re                        # Biblioteca para manejo de expresiones regulares y procesamiento de texto


# ===============================================================
# Librerías para Procesamiento de Imágenes y Umbralización
# ===============================================================
!pip install pillow-heif                         # Instala soporte para imágenes HEIF/HEIC
!pip -q install opencv-python-headless           # Instala OpenCV sin componentes gráficos

from skimage import data                         # Carga imágenes de ejemplo
from skimage.filters import threshold_otsu       # Umbralización automática (Otsu)
from skimage.filters import threshold_multiotsu  # Umbralización multiclase (más de dos niveles)
from skimage.filters import try_all_threshold    # Prueba varios métodos de umbralización

import cv2                                       # Procesamiento de imágenes y visión por computadora
import imageio.v3 as iio                         # Lectura y escritura de imágenes en distintos formatos
from PIL import Image                            # Manipulación y visualización de imágenes
from pillow_heif import register_heif_opener     # Permite abrir imágenes HEIC/HEIF en PIL

register_heif_opener()                           # Activa el soporte HEIF/HEIC en PIL


# ===============================================================
# UTILIDADES Y MANEJO DEL SISTEMA
# ===============================================================
import gdown                                # Descarga de archivos desde Google Drive
import os                                   # Operaciones del sistema operativo
import sys                                  # Parámetros y funciones específicas del sistema
import socket                               # Interfaz de red de bajo nivel
import platform                             # Información sobre la plataforma
import pathlib                              # Manejo orientado a objetos de rutas
from datetime import datetime               # Manejo de fechas y horas
from zoneinfo import ZoneInfo               # Manejo de zonas horarias

# ===============================================================
# MENSAJE DE CONFIRMACIÓN
# ===============================================================
print("✅ Librerías cargadas")
print(f"📅 Timestamp de ejecución: {datetime.now(ZoneInfo('America/Mexico_City')).strftime('%Y-%m-%d %H:%M:%S')}")

try:
    with urlopen('https://ipapi.co/country_name/') as response:
        country = response.read().decode('utf-8').strip()
    print(f"🌍 País de ejecución: {country}")
    with urlopen('https://ipapi.co/region/') as response:
        region = response.read().decode('utf-8').strip()
    print(f"🗺️ Estado/Región de ejecución: {region}")
except Exception as e:
    print("No se pudo determinar el país o estado de ejecución (posiblemente sin conexión a internet).")

print("Python:", sys.version)
print("Ejecutable:", sys.executable)
print("Plataforma:", platform.platform())
print("Hostname:", socket.gethostname())
print("Carpeta trabajo:", os.getcwd())
print("Usuario:", pathlib.Path.home())
Collecting pillow-heif
  Downloading pillow_heif-1.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (9.6 kB)
Requirement already satisfied: pillow>=11.1.0 in /usr/local/lib/python3.12/dist-packages (from pillow-heif) (11.3.0)
Downloading pillow_heif-1.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (5.5 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.5/5.5 MB 32.3 MB/s eta 0:00:00
Installing collected packages: pillow-heif
Successfully installed pillow-heif-1.1.1
✅ Librerías cargadas
📅 Timestamp de ejecución: 2025-10-30 20:54:13
No se pudo determinar el país o estado de ejecución (posiblemente sin conexión a internet).
Python: 3.12.12 (main, Oct 10 2025, 08:52:57) [GCC 11.4.0]
Ejecutable: /usr/bin/python3
Plataforma: Linux-6.6.105+-x86_64-with-glibc2.35
Hostname: 13b6db1eb6b4
Carpeta trabajo: /content
Usuario: /root

📂 Gestión de Carpetas y Descarga de Imágenes¶

Este bloque realiza dos tareas principales:

  1. Limpieza de directorios para asegurar que no existan archivos residuales.
  2. Descarga de imágenes desde Google Drive hacia carpetas locales organizadas.

🧹 1. Limpieza de Directorios Existentes¶

Se define una lista de carpetas que pueden contener imágenes o datos previos.

Para cada carpeta:

  • Se verifica si existe.
  • Si existe, se eliminan todos los archivos y subcarpetas internos.
  • Si no existe, simplemente se omite.

Esto asegura que los resultados previos no interfieran con el nuevo procesamiento.

🔧 Se utiliza os.unlink() para archivos y shutil.rmtree() para subdirectorios.


🗂️ 2. Preparación del Directorio de Descarga¶

Se crea la carpeta imagenes_nuevas si no está presente.
Esta carpeta almacenará las imágenes descargadas en su formato original.


⬇️ 3. Descarga de Imágenes desde Google Drive¶

Se define un diccionario donde:

  • La clave es el nombre del archivo destino.
  • El valor es el ID del archivo en Google Drive.

Luego, se usa gdown.download() para descargar cada imagen de forma automática.


🌁 4. Descarga Adicional en la Carpeta bay¶

Se repite el mismo procedimiento, pero esta vez para la carpeta bay, destinada a otro conjunto de imágenes.

Si la carpeta no existe, se crea.
Luego se descargan las imágenes usando el mismo esquema de ID.


✅ Resultado¶

Al finalizar, el entorno queda con:

Carpeta Contenido
imagenes_nuevas/ Primer grupo de imágenes descargadas
bay/ Segundo grupo de imágenes descargadas
Resto de carpetas (data/, p2_*) Limpias y listas para procesamiento
In [ ]:
# ===============================================================
# Limpiar todos los archivos en las carpetas especificadas
# ===============================================================
print("\n--- Limpiando archivos en las carpetas especificadas ---")
directorios_a_limpiar = ["imagenes_nuevas", "data", "p2_coins", "p2b_plates", "p2c_wheresimba", "bay"]

for directorio in directorios_a_limpiar:
    if os.path.exists(directorio):
        print(f"Eliminando contenido de la carpeta '{directorio}'...")
        for filename in os.listdir(directorio):
            file_path = os.path.join(directorio, filename)
            try:
                if os.path.isfile(file_path) or os.path.islink(file_path):
                    os.unlink(file_path)  # Eliminar archivo o enlace simbólico
                elif os.path.isdir(file_path):
                    shutil.rmtree(file_path)  # Eliminar subcarpetas
            except Exception as e:
                print(f"Error al eliminar {file_path}: {e}")
        print(f"Carpeta '{directorio}' limpiada.")
    else:
        print(f"La carpeta '{directorio}' no existe, no requiere limpieza.")


# ===============================================================
#  PARTE 1: DESCARGA LISTA DE IMÁGENES
# ===============================================================

# --- 1. Preparar el directorio de salida original ---
output_dir_original = "imagenes_nuevas"
if not os.path.exists(output_dir_original):
    os.makedirs(output_dir_original)
    print(f"Carpeta '{output_dir_original}' creada.")

# --- 2. Definir las imágenes a descargar ---
imagenes_a_descargar = {
    "01 - Cerro de a Silla Monterrey.HEIC": "1anUZCFwSnZII15MDqGb5sApYhmoXqx18",
    "02 - The Killers Las Vegas.HEIC": "1iizEGyZRAQJ-zcu4MOMdo_OATKvQ8HMr",
    "03 - Bellagio at Night.HEIC": "1u7N9au9fdDat9GYgK215SrSYfWVMLvSK",
    "04 - The Great Canyon.heic": "1zmudHsvMzDmITmQljkq7MOeAoaJj4gxo",
    "05 - The Cosmopolitan Las Vegas.HEIC": "1JymKRxsTuiIeuviO9RwotmQqRJ838V-m",
    "06 - Chinese Dragon.heic": "1rdBFw1yUmmXQqFca6iUppbYgSgzGorge",
    "07 - The Park Las Vegas.HEIC": "1BJD1hWP17PsF7pelR7zCW91sNb5cK31m",
    "08 - Simba Snow.HEIC": "1BOLxQGbKCZtRwA6wVyCBaKgowZfBrJGD"
}

print("\n--- Iniciando la descarga de la lista de imágenes ---")

# --- 3. Bucle para descargar cada imagen de la lista ---
for filename, file_id in imagenes_a_descargar.items():
    output_path = os.path.join(output_dir_original, filename)
    print(f"Descargando '{filename}'...")
    gdown.download(id=file_id, output=output_path, quiet=False)


# --- 4. Se repiten los pasos 1, 2 y 3 para otra carpeta de imagenes ---
output_dir_original = "bay"
if not os.path.exists(output_dir_original):
    os.makedirs(output_dir_original)
    print(f"Carpeta '{output_dir_original}' creada.")
imagenes_a_descargar = {
    "bay_coittower.heic": "1iFjGeiMmJukD8olvg9hLiGt6MHo7doGh",
    "bay_ferry.heic": "1gvdXLQwSGkQ8S2zb-NARRmX_in4fJwMy",
    "bay_goldengate.jpeg": "14mICfajLYyKzaVGH9zUZkuAmTo8QT-7j",
    "bay_lifebuoy.jpeg": "1rkrXljdLPA7NpuA-GtLbFcFrzX2ns1O4",
    "bay_sailboats.jpeg": "1Qq4cMtXN6dnUSMdBj2PMn_oc5vzcRvvM",
    "bay_sanfrancisco.heic": "1z-QuwqPhShxBBqwH7VG5i3dniEutLgdS",
    "bay_sausalito.jpeg": "19Rsp858V4zbi5nxqiOl6dKJ3kRdTr8vd",
    "bay_seagull.heic": "1acKuzQN9w6R5KmhoFEKINP5nCw507ZHw",
    "bay_skyline": "16w_Vj7byo5uiFJhIMO5dipvPXqKbqL-E",
    "bay_sunset": "1augsryFJdJYtByVxarHImjJByZAvUBL1",
    "bay_wheel": "1JJDEUG20SKoOTyeBRIViWgKunVnHlMW_"
}
for filename, file_id in imagenes_a_descargar.items():
    output_path = os.path.join(output_dir_original, filename)
    print(f"Descargando '{filename}'...")
    gdown.download(id=file_id, output=output_path, quiet=False)


print("\n✅ ¡Descarga de la lista completada!")
--- Limpiando archivos en las carpetas especificadas ---
La carpeta 'imagenes_nuevas' no existe, no requiere limpieza.
La carpeta 'data' no existe, no requiere limpieza.
La carpeta 'p2_coins' no existe, no requiere limpieza.
La carpeta 'p2b_plates' no existe, no requiere limpieza.
La carpeta 'p2c_wheresimba' no existe, no requiere limpieza.
La carpeta 'bay' no existe, no requiere limpieza.
Carpeta 'imagenes_nuevas' creada.

--- Iniciando la descarga de la lista de imágenes ---
Descargando '01 - Cerro de a Silla Monterrey.HEIC'...
Downloading...
From: https://drive.google.com/uc?id=1anUZCFwSnZII15MDqGb5sApYhmoXqx18
To: /content/imagenes_nuevas/01 - Cerro de a Silla Monterrey.HEIC
100%|██████████| 1.40M/1.40M [00:00<00:00, 99.1MB/s]
Descargando '02 - The Killers Las Vegas.HEIC'...
Downloading...
From: https://drive.google.com/uc?id=1iizEGyZRAQJ-zcu4MOMdo_OATKvQ8HMr
To: /content/imagenes_nuevas/02 - The Killers Las Vegas.HEIC
100%|██████████| 2.06M/2.06M [00:00<00:00, 49.0MB/s]
Descargando '03 - Bellagio at Night.HEIC'...
Downloading...
From: https://drive.google.com/uc?id=1u7N9au9fdDat9GYgK215SrSYfWVMLvSK
To: /content/imagenes_nuevas/03 - Bellagio at Night.HEIC
100%|██████████| 4.16M/4.16M [00:00<00:00, 53.0MB/s]
Descargando '04 - The Great Canyon.heic'...
Downloading...
From: https://drive.google.com/uc?id=1zmudHsvMzDmITmQljkq7MOeAoaJj4gxo
To: /content/imagenes_nuevas/04 - The Great Canyon.heic
100%|██████████| 1.37M/1.37M [00:00<00:00, 44.1MB/s]
Descargando '05 - The Cosmopolitan Las Vegas.HEIC'...
Downloading...
From: https://drive.google.com/uc?id=1JymKRxsTuiIeuviO9RwotmQqRJ838V-m
To: /content/imagenes_nuevas/05 - The Cosmopolitan Las Vegas.HEIC
100%|██████████| 4.16M/4.16M [00:00<00:00, 113MB/s]
Descargando '06 - Chinese Dragon.heic'...
Downloading...
From: https://drive.google.com/uc?id=1rdBFw1yUmmXQqFca6iUppbYgSgzGorge
To: /content/imagenes_nuevas/06 - Chinese Dragon.heic
100%|██████████| 3.49M/3.49M [00:00<00:00, 183MB/s]
Descargando '07 - The Park Las Vegas.HEIC'...
Downloading...
From: https://drive.google.com/uc?id=1BJD1hWP17PsF7pelR7zCW91sNb5cK31m
To: /content/imagenes_nuevas/07 - The Park Las Vegas.HEIC
100%|██████████| 4.88M/4.88M [00:00<00:00, 147MB/s]
Descargando '08 - Simba Snow.HEIC'...
Downloading...
From: https://drive.google.com/uc?id=1BOLxQGbKCZtRwA6wVyCBaKgowZfBrJGD
To: /content/imagenes_nuevas/08 - Simba Snow.HEIC
100%|██████████| 1.79M/1.79M [00:00<00:00, 150MB/s]
Carpeta 'bay' creada.
Descargando 'bay_coittower.heic'...
Downloading...
From: https://drive.google.com/uc?id=1iFjGeiMmJukD8olvg9hLiGt6MHo7doGh
To: /content/bay/bay_coittower.heic
100%|██████████| 1.18M/1.18M [00:00<00:00, 108MB/s]
Descargando 'bay_ferry.heic'...
Downloading...
From: https://drive.google.com/uc?id=1gvdXLQwSGkQ8S2zb-NARRmX_in4fJwMy
To: /content/bay/bay_ferry.heic
100%|██████████| 2.19M/2.19M [00:00<00:00, 149MB/s]
Descargando 'bay_goldengate.jpeg'...
Downloading...
From: https://drive.google.com/uc?id=14mICfajLYyKzaVGH9zUZkuAmTo8QT-7j
To: /content/bay/bay_goldengate.jpeg
100%|██████████| 768k/768k [00:00<00:00, 103MB/s]
Descargando 'bay_lifebuoy.jpeg'...
Downloading...
From: https://drive.google.com/uc?id=1rkrXljdLPA7NpuA-GtLbFcFrzX2ns1O4
To: /content/bay/bay_lifebuoy.jpeg
100%|██████████| 601k/601k [00:00<00:00, 58.5MB/s]
Descargando 'bay_sailboats.jpeg'...
Downloading...
From: https://drive.google.com/uc?id=1Qq4cMtXN6dnUSMdBj2PMn_oc5vzcRvvM
To: /content/bay/bay_sailboats.jpeg
100%|██████████| 725k/725k [00:00<00:00, 87.0MB/s]
Descargando 'bay_sanfrancisco.heic'...
Downloading...
From: https://drive.google.com/uc?id=1z-QuwqPhShxBBqwH7VG5i3dniEutLgdS
To: /content/bay/bay_sanfrancisco.heic
100%|██████████| 1.73M/1.73M [00:00<00:00, 139MB/s]
Descargando 'bay_sausalito.jpeg'...
Downloading...
From: https://drive.google.com/uc?id=19Rsp858V4zbi5nxqiOl6dKJ3kRdTr8vd
To: /content/bay/bay_sausalito.jpeg
100%|██████████| 883k/883k [00:00<00:00, 97.0MB/s]
Descargando 'bay_seagull.heic'...
Downloading...
From: https://drive.google.com/uc?id=1acKuzQN9w6R5KmhoFEKINP5nCw507ZHw
To: /content/bay/bay_seagull.heic
100%|██████████| 938k/938k [00:00<00:00, 77.3MB/s]
Descargando 'bay_skyline'...
Downloading...
From: https://drive.google.com/uc?id=16w_Vj7byo5uiFJhIMO5dipvPXqKbqL-E
To: /content/bay/bay_skyline
100%|██████████| 1.63M/1.63M [00:00<00:00, 138MB/s]
Descargando 'bay_sunset'...
Downloading...
From: https://drive.google.com/uc?id=1augsryFJdJYtByVxarHImjJByZAvUBL1
To: /content/bay/bay_sunset
100%|██████████| 771k/771k [00:00<00:00, 95.0MB/s]
Descargando 'bay_wheel'...
Downloading...
From: https://drive.google.com/uc?id=1JJDEUG20SKoOTyeBRIViWgKunVnHlMW_
To: /content/bay/bay_wheel
100%|██████████| 2.37M/2.37M [00:00<00:00, 153MB/s]
✅ ¡Descarga de la lista completada!

Umbralización única ¶

🎯 Umbralización con el Método de Otsu¶

La umbralización es una técnica de segmentación que convierte una imagen en escala de grises en una imagen binaria, separando regiones según la intensidad de los píxeles.
El método de Otsu ✨ automatiza esta decisión al buscar el umbral que mejor distingue dos clases de píxeles: fondo y objeto.


📊 Principio Estadístico¶

Otsu define un criterio de optimización basado en:

  • Minimizar la varianza intra-clase (las clases sean internamente homogéneas)
  • Maximizar la varianza inter-clase (las clases sean lo más distintas posible)

En términos simples: se elige el umbral que separa mejor el histograma de la imagen.


🧮 Fórmulas Clave¶

Probabilidad de clase:

  • $\omega_0(t)$ → proporción de píxeles con intensidad ≤ $t$
  • $\omega_1(t)$ → proporción de píxeles con intensidad > $t$

Media de intensidad en cada clase:

  • $\mu_0(t)$ → media de intensidades de la clase 0
  • $\mu_1(t)$ → media de intensidades de la clase 1

Varianza inter-clase:

$\sigma_b^2(t) = \omega_0(t) \cdot \omega_1(t) \cdot [\mu_0(t) - \mu_1(t)]^2$

👉 El umbral óptimo es aquel que maximiza $\sigma_b^2(t)$.


✅ Condiciones de Aplicabilidad¶

  • Ideal para imágenes con histograma bimodal, donde los objetos y el fondo tienen intensidades claramente diferenciadas.
  • Menos efectivo en imágenes con ruido, iluminación desigual o múltiples regiones de interés.
Algoritmo Tipo Principio Ideal para…
Otsu Automático, único Maximiza la varianza entre clases Imágenes con histograma bimodal
Li Automático, único Minimiza la entropía cruzada Imágenes con ruido o gradientes suaves
Yen Automático, único Maximiza la entropía de la imagen Segmentación con contraste alto
Triangle Automático, único Geometría del histograma Distribuciones asimétricas
Mean Manual, único Promedio de intensidades Casos simples o controlados
Minimum Automático, único Busca mínimos locales en el histograma Imágenes con múltiples modos
Isodata Iterativo, único Promedia clases hasta converger Distribuciones complejas
Multi-Otsu Automático, múltiple Extiende Otsu a múltiples clases Segmentación en más de dos regiones

🔎 En resumen, Otsu es una técnica eficiente y totalmente automática, pero su desempeño depende de la calidad del contraste y la distribución tonal de la imagen.

In [ ]:
# Cargar imagen de ejemplo
image = data.camera()

# Calcular umbral óptimo con Otsu
thresh = threshold_otsu(image)
binary = image > thresh

# Crear figura con 3 columnas
fig, axes = plt.subplots(ncols=3, figsize=(12, 4))
ax = axes.ravel()

# Imagen original
ax[0].imshow(image, cmap='gray')
ax[0].set_title('Imagen original')
ax[0].axis('off')

# Histograma con línea de umbral
ax[1].hist(image.ravel(), bins=256, color='blue', alpha=0.7)
ax[1].axvline(thresh, color='red', linestyle='--', linewidth=2, label=f'Umbral = {thresh:.0f}')
ax[1].set_title('Histograma de intensidades')
ax[1].set_xlabel('Intensidad')
ax[1].set_ylabel('Frecuencia')
ax[1].legend()

# Imagen umbralizada
ax[2].imshow(binary, cmap='gray')
ax[2].set_title('Imagen binarizada (Otsu)')
ax[2].axis('off')

# Ajustar espaciado
plt.tight_layout()
plt.show()
No description has been provided for this image

🔍 Comparación Visual de Métodos de Umbralización¶

Este fragmento de código permite comparar visualmente múltiples algoritmos de umbralización aplicados a una imagen en escala de grises, utilizando la función try_all_threshold.
Es especialmente útil cuando se quiere elegir el mejor método sin necesidad de conocer en detalle las bases matemáticas de cada algoritmo.
El resultado es una rejilla de imágenes, cada una mostrando cómo segmenta la imagen un método distinto.


🎛️ Diferencias Entre Métodos de Umbralización¶

Cada método tiene suposiciones diferentes sobre la distribución de intensidades en la imagen:

Método Supuesto / Característica Comentario
Otsu Histograma bimodal Funciona bien cuando fondo y objeto son claramente distintos.
Li / Yen Optimización basada en entropía o energía Útiles cuando el contraste no es evidente.
Triangle Basado en la geometría del histograma Puede ser más robusto ante ruido o asimetrías.
Mean Simple promedio global Rápido pero menos preciso si la iluminación no es uniforme.

🎯 ¿Por qué usar try_all_threshold?¶

  • Permite comparar métodos lado a lado.
  • Facilita la selección informada del mejor enfoque.
  • Ahorra tiempo cuando la imagen presenta variaciones de iluminación o ruido.
  • Útil en procesamiento exploratorio o análisis inicial de dataset.

✨ En resumen, esta herramienta es ideal para experimentar y observar cómo distintos algoritmos interpretan la misma imagen, permitiendo elegir el umbral más consistente con los objetivos de segmentación.

In [ ]:
# Cargar imagen
img = data.page()

# Aplicar todos los métodos de umbralización disponibles
fig, ax = try_all_threshold(img, figsize=(14, 10), verbose=False)

for a in ax:
    a.set_title(a.get_title(), fontsize=10)
    a.axis('off')
    a.images[0].set_cmap('gray')

plt.tight_layout()
plt.show()
No description has been provided for this image

Umbralización múltiple ¶

🎨 Segmentación Multiclase con Multi-Otsu¶

Multi-Otsu es una extensión del método clásico de Otsu (1979) que permite segmentar una imagen en más de dos clases mediante la obtención de múltiples umbrales óptimos.
Mientras el Otsu tradicional determina un solo umbral para dividir la imagen en fondo vs objeto, Multi-Otsu generaliza el criterio para K clases, calculando K − 1 umbrales que dividen el histograma en regiones estadísticamente diferenciables.


🧠 Idea Principal¶

El algoritmo selecciona los umbrales que maximizan la varianza inter-clase total, es decir:

  • Cada clase debe ser internamente homogénea.
  • Las clases deben ser lo más distintas posible entre sí en términos de intensidad.

Esto es especialmente útil para imágenes con múltiples regiones relevantes, como tonos de piel, niveles de iluminación, o diferentes materiales en una escena.


🧮 Formulación¶

Para una imagen con L niveles de gris y K clases, se buscan $K - 1$ umbrales $t_1, t_2, ..., t_{K-1}$ que maximicen:

$\sigma_b^2 = \sum_{k=1}^{K} \omega_k (\mu_k - \mu_T)^2$

donde:

Símbolo Significado
$\omega_k$ probabilidad (frecuencia relativa) de la clase k
$\mu_k$ media de intensidades de la clase k
$\mu_T$ media global de toda la imagen

✅ ¿Cuándo usar Multi-Otsu?¶

Situación Adecuado
La imagen contiene 3 o más regiones claramente diferenciables ✔️ Sí
La iluminación es relativamente uniforme ✔️ Sí
La imagen es ruidosa o poco contrastada ⚠️ Puede degradarse el desempeño

✨ En resumen, Multi-Otsu permite segmentación multiclase automática y estadísticamente óptima, ideal para análisis donde la imagen contiene varias regiones de interés, sin necesidad de definir umbrales manualmente.

In [ ]:
# Cargar imagen
image = data.camera()

# Aplicar Multi-Otsu para generar tres clases
thresholds = threshold_multiotsu(image)

# Clasificar píxeles según los umbrales obtenidos
regions = np.digitize(image, bins=thresholds)

# Crear figura con tres subgráficos
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(14, 4))
titles = ['Imagen original', 'Histograma con umbrales', 'Segmentación Multi-Otsu']

# Imagen original
ax[0].imshow(image, cmap='gray')
ax[0].set_title(titles[0], fontsize=12)
ax[0].axis('off')

# Histograma con líneas de umbral
ax[1].hist(image.ravel(), bins=256, color='steelblue', alpha=0.7)
for thresh in thresholds:
    ax[1].axvline(thresh, color='darkred', linestyle='--', linewidth=2, label=f'Umbral = {thresh}')
ax[1].set_title(titles[1], fontsize=12)
ax[1].set_xlabel('Intensidad')
ax[1].set_ylabel('Frecuencia')
ax[1].legend()

# Imagen segmentada con mapa de color perceptual
ax[2].imshow(regions, cmap='jet')
ax[2].set_title(titles[2], fontsize=12)
ax[2].axis('off')

plt.tight_layout()
plt.show()
No description has been provided for this image

🌉 Aplicación de Umbralización en Imágenes Reales y Personalizadas¶

En esta sección se retoma el flujo de trabajo desarrollado anteriormente para realizar umbralización y segmentación sobre imágenes en escala de grises.
Inicialmente, se trabajó con imágenes de ejercicios previos (por ejemplo, escenas urbanas y arte en Las Vegas).
Ahora, se amplía el análisis incorporando fotografías capturadas desde la Bahía de San Francisco, tomadas durante el recorrido en ferry desde el Muelle 1 hasta Sausalito y de regreso 🚢.

Estas nuevas imágenes incluyen vistas icónicas como:

  • El Golden Gate Bridge 🌉,
  • El skyline de San Francisco 🏙️,
  • Barcos y velas en la bahía ⛵,
  • Elementos marítimos como salvavidas y muelles,
  • La zona de Sausalito y sus paisajes costeros.

El objetivo es observar cómo los métodos de Otsu y Multi-Otsu se comportan en escenas con variaciones de iluminación, reflejos, texturas y regiones complejas, comparadas con imágenes más controladas.


La siguiente función visualizar_resultados() permite analizar y comparar el efecto de aplicar Otsu y Multi-Otsu sobre una imagen en escala de grises.
Su objetivo es mostrar, de manera visual y clara, cómo cambian tanto los umbrales como el resultado de la segmentación.

La figura generada presenta 5 paneles que resumen el proceso completo:


🔍 Desglose de la Visualización¶

Panel Contenido Descripción
1. Imagen original cmap='gray' Muestra la imagen en escala de grises tal como fue cargada. Sirve como referencia inicial.
2. Histograma + Umbral (Otsu) axvline(umbral) Se grafica el histograma de intensidades y se marca el umbral óptimo calculado con Otsu. Permite observar si la distribución es bimodal.
3. Imagen binaria cmap='gray' Después de aplicar el umbral, se genera una imagen blanco/negro que separa objeto y fondo. Resultado directo de Otsu.
4. Histograma Multiclase (Multi-Otsu) Líneas verticales múltiples Se vuelven a graficar las intensidades, pero ahora se marcan múltiples umbrales (K−1) obtenidos con Multi-Otsu.
5. Imagen segmentada multiclase cmap='jet' La imagen se divide en regiones diferenciadas por color, asignando una etiqueta (clase) a cada pixel. Esto permite distinguir más de dos grupos visualmente.

🎯 Propósito del Ejemplo¶

Esta visualización permite:

  • Comparar fácilmente la segmentación binaria vs multiclase.
  • Observar la relación entre el histograma y los umbrales obtenidos.
  • Identificar regiones dentro de la imagen que podrían representar materiales, niveles de iluminación, texturas o áreas de interés.
  • Tomar decisiones sobre qué técnica es más adecuada para el problema.

✨ Resultado Esperado¶

Se obtiene una representación clara del proceso completo de segmentación:

  • Del espacio de intensidades → al umbral → a regiones clasificadas.

Esto facilita la interpretación del comportamiento de los algoritmos y ayuda en tareas como detección, análisis de patrones y segmentación semántica básica.

In [ ]:
def visualizar_resultados(imagen_array, umbral, imagen_binaria, thresholds, regions):
    fig, ax = plt.subplots(nrows=1, ncols=5, figsize=(18, 4))

    # 1) Imagen original en escala de grises
    ax[0].imshow(imagen_array, cmap='gray')
    ax[0].set_title("Imagen original", fontsize=11)
    ax[0].axis('off')

    # 2) Histograma con umbral simple
    ax[1].hist(imagen_array.ravel(), bins=256, color='steelblue', alpha=0.7)
    ax[1].axvline(umbral, color='darkred', linestyle='--', linewidth=2, label=f'Umbral = {umbral:.2f}')
    ax[1].set_title("Histograma (Otsu)", fontsize=11)
    ax[1].set_xlabel("Intensidad")
    ax[1].set_ylabel("Frecuencia")
    ax[1].legend()

    # 3) Imagen binaria (usar cmap='gray')
    ax[2].imshow(imagen_binaria, cmap='gray')
    ax[2].set_title("Imagen binaria", fontsize=11)
    ax[2].axis('off')

    # 4) Histograma con multi-umbrales
    ax[3].hist(imagen_array.ravel(), bins=256, color='steelblue', alpha=0.7)
    for t in thresholds:
        ax[3].axvline(t, color='darkred', linestyle='--', linewidth=2)
    ax[3].set_title("Histograma (Multi-Otsu)", fontsize=11)
    ax[3].set_xlabel("Intensidad")

    # 5) Imagen segmentada por múltiples regiones usando 'jet'
    ax[4].imshow(regions, cmap='jet')
    ax[4].set_title("Regiones (Multi-Otsu)", fontsize=11)
    ax[4].axis('off')

    plt.tight_layout()
    plt.show()

🔄 Flujo de Procesamiento en Cada Imagen¶

Para cada ruta en la lista:

  1. Se lee la imagen en formato RGB (iio.imread).
  2. Se convierte a escala de grises (convert("L")).
  3. Se obtiene el arreglo numérico (np.array).
  4. Se aplica Otsu para obtener un umbral binario.
  5. Se aplica Multi-Otsu para obtener múltiples umbrales.
  6. Se segmenta la imagen en regiones etiquetadas (np.digitize).
  7. Se visualizan los resultados completos con visualizar_resultados().
In [ ]:
rutas_imagenes = [
    os.path.join("imagenes_nuevas", "05 - The Cosmopolitan Las Vegas.HEIC"),
    os.path.join("imagenes_nuevas", "06 - Chinese Dragon.heic"),
    os.path.join("imagenes_nuevas", "07 - The Park Las Vegas.HEIC"),
    os.path.join("bay", "bay_coittower.heic"),
    os.path.join("bay", "bay_ferry.heic"),
    os.path.join("bay", "bay_goldengate.jpeg"),
    os.path.join("bay", "bay_lifebuoy.jpeg"),
    os.path.join("bay", "bay_sailboats.jpeg"),
    os.path.join("bay", "bay_sanfrancisco.heic"),
    os.path.join("bay", "bay_sausalito.jpeg"),
    os.path.join("bay", "bay_seagull.heic"),
    os.path.join("bay", "bay_skyline"),
    os.path.join("bay", "bay_sunset"),
    os.path.join("bay", "bay_wheel")
]

for ruta in rutas_imagenes:
    imagen_rgb = iio.imread(ruta)
    imagen_pil = Image.fromarray(imagen_rgb)
    imagen_gris = imagen_pil.convert("L")
    imagen_array = np.array(imagen_gris)
    umbral = threshold_otsu(imagen_array)
    imagen_binaria = imagen_array > umbral
    thresholds = threshold_multiotsu(imagen_array)
    regions = np.digitize(imagen_array, bins=thresholds)
    visualizar_resultados(imagen_array, umbral, imagen_binaria, thresholds, regions)
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image

🎯 Resultado y Observación¶

Este conjunto de pruebas permite comparar el desempeño de los algoritmos en diferentes contextos visuales:

  • Escenas de interior vs. exterior
  • Iluminación artificial vs. iluminación natural
  • Objetos con bordes definidos vs. paisajes con transiciones suaves
  • Composiciones simples vs. entornos complejos como la bahía de San Francisco

El contraste entre estos casos ayuda a evaluar la sensibilidad y las limitaciones de la segmentación automática, mostrando cómo el método puede comportarse de manera diferente según la textura, contraste y distribución tonal de la imagen.

Este análisis también abre la puerta a mejoras futuras, como:

  • Aplicar filtros de suavizado o reducción de ruido antes de segmentar,
  • Realizar ecualización de histograma para mejorar el contraste,
  • Explorar segmentación basada en textura o patrones, especialmente útil en paisajes naturales y escenas urbanas detalladas.

🧭 Comparación Global de Métodos de Umbralización¶

Además de aplicar Otsu y Multi-Otsu, se ejecuta una comparación amplia con try_all_threshold para probar automáticamente múltiples algoritmos sobre la misma imagen.
Esto se aplicó tanto a imágenes de ejercicios anteriores como a las capturadas durante el recorrido en ferry por la Bahía de San Francisco (vistas del Golden Gate, el skyline, muelles y embarcaciones) 🚢🌉.


🔍 Qué hace ahora el flujo¶

  • Se ejecuta try_all_threshold sin mostrar la figura temporal (modo no interactivo).
  • Se extraen los resultados (título y binarización) de cada método.
  • Se cierran todas las figuras pendientes para evitar que Colab muestre la rejilla 2×4.
  • Se crea una única figura final en formato 1×8 por imagen, con todos los métodos alineados horizontalmente.

Resultado: por cada imagen se obtiene una sola fila con 8 paneles comparables (mismo cmap='gray', títulos normalizados y ejes ocultos).


🎯 Valor del análisis¶

Beneficio Descripción
Comparación directa Una fila horizontal 1×8 facilita ver diferencias entre métodos en la misma escala visual.
Selección informada Permite elegir el algoritmo más estable ante reflejos, sombras y texturas reales.
Control en Colab Evita figuras duplicadas y asegura una única salida limpia por imagen.

✨ En conjunto, esta configuración proporciona una vista clara y compacta del comportamiento de los algoritmos de umbralización, adecuada para evaluar escenas urbanas y marítimas con variaciones reales de iluminación y contraste.

In [ ]:
for ruta in rutas_imagenes:
    print(f"\nProcesando: {ruta}")

    # --- Cargar y a gris ---
    imagen_rgb = iio.imread(ruta)
    imagen_pil = Image.fromarray(imagen_rgb)
    imagen_gris = imagen_pil.convert("L")
    imagen_array = np.array(imagen_gris)

    # --- 1) Ejecutar try_all_threshold SIN mostrar nada ---
    with plt.ioff():
        fig_tmp, ax_tmp = try_all_threshold(imagen_array, figsize=(12, 8), verbose=False)

    # Aplanar ejes y extraer (título, imagen)
    axes_list = np.array(ax_tmp).ravel().tolist()
    results = [(a.get_title(), a.images[0].get_array()) for a in axes_list]
    results = results[:8]  # fuerza 8 métodos
    n_methods = len(results)

    # --- 2) Cerrar cualquier figura pendiente para que Colab no la muestre ---
    plt.close('all')

    # --- 3) Crear la figura final: 1 fila x 8 columnas ---
    fig, axes = plt.subplots(nrows=1, ncols=n_methods, figsize=(3*n_methods, 3))
    if n_methods == 1:
        axes = [axes]

    for ax, (title, img_bin) in zip(axes, results):
        ax.imshow(img_bin, cmap='gray', interpolation='nearest')
        ax.set_title(title, fontsize=9)
        ax.axis('off')

    fig.suptitle(f"Comparación de Umbralización (1x{n_methods}) - {os.path.basename(ruta)}", fontsize=12)

    # --- 4) Mostrar figura ---
    plt.tight_layout()
    plt.show()
Procesando: imagenes_nuevas/05 - The Cosmopolitan Las Vegas.HEIC
No description has been provided for this image
Procesando: imagenes_nuevas/06 - Chinese Dragon.heic
No description has been provided for this image
Procesando: imagenes_nuevas/07 - The Park Las Vegas.HEIC
No description has been provided for this image
Procesando: bay/bay_coittower.heic
No description has been provided for this image
Procesando: bay/bay_ferry.heic
No description has been provided for this image
Procesando: bay/bay_goldengate.jpeg
No description has been provided for this image
Procesando: bay/bay_lifebuoy.jpeg
No description has been provided for this image
Procesando: bay/bay_sailboats.jpeg
No description has been provided for this image
Procesando: bay/bay_sanfrancisco.heic
No description has been provided for this image
Procesando: bay/bay_sausalito.jpeg
No description has been provided for this image
Procesando: bay/bay_seagull.heic
No description has been provided for this image
Procesando: bay/bay_skyline
No description has been provided for this image
Procesando: bay/bay_sunset
No description has been provided for this image
Procesando: bay/bay_wheel
No description has been provided for this image

🎯 Mini-Proyecto: Detección y Segmentación de Códigos QR utilizando Umbralización de Otsu¶

📌 Introducción¶

Los códigos QR (Quick Response) son patrones bidimensionales que almacenan información mediante módulos en blanco y negro. Se utilizan ampliamente debido a su capacidad de codificar datos de forma compacta y ser leídos rápidamente, incluso desde cámaras de baja calidad 📷.

Estos códigos pueden contener:

  • 🌐 URLs
  • 📝 Texto (mensajes, nombres, instrucciones)
  • 👤 Información de contacto (vCards)
  • 🏷️ Identificadores de objetos o activos
  • 🔗 Referencias para sistemas logísticos o industriales

Para procesarlos de manera eficaz, es importante lograr una separación clara entre el patrón del código y el fondo. Aquí es donde el método de Otsu resulta muy útil.


⚙️ ¿Por qué Otsu para Códigos QR?¶

El método de Otsu determina automáticamente el umbral óptimo para convertir una imagen en escala de grises en una imagen binaria. Esto ayuda a resaltar los módulos (cuadrados) del QR con claridad.

✅ Ventajas clave:¶

  • Sin parámetros manuales → ideal para automatización.
  • Bajo costo computacional → apto para dispositivos móviles o sistemas embebidos.
  • Maximiza la separación estadística entre fondo y patrón.
  • Mejora la robustez en la extracción de contornos para que el QR pueda ser decodificado correctamente.

En otras palabras, Otsu ayuda a producir una imagen binaria limpia, lo cual facilita la detección y lectura del código QR 🔍.


🧪 Casos de Uso incluidos en este mini-proyecto¶

Se utilizarán tres ejemplos prácticos para demostrar diferentes escenarios:

1) 🟩 QR Limpio Generado en Página Web¶

  • Imagen con un código QR nítido y fondo uniforme.
  • Contenido: un link a una página web.
  • Objetivo: demostrar el flujo base →
    imagen → escala de grises → Otsu → binarización → detección → decodificación.

2) 🏷️ Varios Códigos QR con Nombres del Equipo¶

  • Imagen con múltiples códigos QR, cada uno con el nombre de un integrante.
  • Caso práctico: marcar objetos personales (celulares, cargadores, laptops).
  • Permite observar cómo Otsu puede separar correctamente varios códigos en la misma escena.

3) 🤖 Código QR en Entorno Industrial¶

  • Imagen de una parte de un robot industrial identificada mediante QR.
  • Aplicación común:
    trazabilidad de componentes, control de inventario, mantenimiento programado.
  • Aquí se observarán desafíos como:
    • Iluminación variable 💡
    • Reflejos metálicos ✨
    • Texturas complejas 🔩

🎯 Objetivo del Mini-Proyecto¶

  • Aplicar Otsu como método de preprocesamiento para mejorar la lectura y reconocimiento de códigos QR.
  • Analizar cómo cambia el desempeño al pasar de escenarios controlados a escenarios reales.
  • Relacionar la binarización con aplicaciones prácticas en:
    • Identidad digital personal
    • Organización de objetos
    • Industria y manufactura inteligente

🏁 Resultado Esperado¶

Habilidad Resultado
🧠 Comprender Otsu Saber cuándo y por qué usarlo en imágenes para QR
🖼️ Preprocesar imágenes Convertir imágenes reales a binario de alta calidad
🔍 Decodificar QR Extraer correctamente la información contenida
🏭 Aplicar a contexto real Identificar casos donde esto mejora procesos logísticos e industriales

✨ En resumen, Otsu no solo convierte una imagen a blanco y negro:
permite estructurar la información visual de una manera útil para la identificación, trazabilidad y automatización.

🔧 Implementación¶

Se agregaron 4 funciones para manejar imágenes y detectar códigos QR. Primero se descargan📥 los recursos desde Google Drive, esta vez desde una carpeta, en caso de que sean muchas imágenes para analizar. Luego se enumeran🗂️ los archivos disponibles, después se detectan y decodifican 🔍 los códigos QR presentes en las imágenes, y finalmente se visualizan 🎨 resaltando su posición y contenido. De esta manera, se cubre el proceso completo: adquisición → organización → análisis → presentación.


download_drive_folder_public(folder_url_or_id, dest_dir) 📁⬇️¶

Descarga una carpeta pública completa de Google Drive (usando URL o ID) y guarda todo su contenido en dest_dir.
Retorno: No regresa valor; solo descarga y confirma la ruta.


list_folder_contents(base_folder) 🗂️¶

Recorre una carpeta y genera una lista ordenada de las rutas de todos los archivos dentro de ella (incluye subcarpetas).
Retorno: list[str] con rutas de archivos.


decode_qr_all(img_uint8) 🔍📦¶

Detecta y decodifica uno o múltiples códigos QR en una imagen dada. Si la detección múltiple falla, intenta detección simple.
Retorno: [(texto_decodificado, coordenadas_esquinas), ...].


draw_qr_boxes(ax, base_img_rgb, results) 🎨🧩¶

Muestra la imagen original y dibuja cuadros y etiquetas sobre cada código QR detectado, usando sus coordenadas.
Retorno: No regresa valor; modifica la visualización en el ax.


In [ ]:
def download_drive_folder_public(folder_url_or_id: str, dest_dir: str):
    import gdown
    os.makedirs(dest_dir, exist_ok=True)

    # Accept URL or raw ID
    if re.search(r'/folders/([a-zA-Z0-9_-]{10,})', folder_url_or_id):
        url = folder_url_or_id
    else:
        url = f"https://drive.google.com/drive/folders/{folder_url_or_id}"

    # remaining_ok allows partial downloads to resume
    gdown.download_folder(url=url, output=dest_dir, quiet=False, use_cookies=False, remaining_ok=True)
    print(f"Done. Files saved under: {os.path.abspath(dest_dir)}")

def list_folder_contents(base_folder: str) -> list:
    file_list = []
    for root, _, files in os.walk(base_folder):
        for f in files:
            relative_path = os.path.join(root, f)
            file_list.append(relative_path)
    return sorted(file_list)


def decode_qr_all(img_uint8):
    det = cv2.QRCodeDetector()
    results = []
    try:
        retval, decoded_info, points, _ = det.detectAndDecodeMulti(img_uint8)
        if retval and points is not None:
            for txt, pts in zip(decoded_info, points):
                if txt:
                    results.append((txt, pts))
    except Exception:
        pass
    if not results:
        txt, pts, _ = det.detectAndDecode(img_uint8)
        if txt:
            results.append((txt, pts))
    return results

def draw_qr_boxes(ax, base_img_rgb, results):
    ax.imshow(base_img_rgb)
    ax.axis('off')
    for txt, pts in results:
        if pts is None:
            continue
        pts = pts.reshape(-1, 2).astype(int)
        poly = np.vstack([pts, pts[0]])
        ax.plot(poly[:,0], poly[:,1], linewidth=2)
        ax.text(pts[0,0], pts[0,1]-5, txt[:40], fontsize=9, bbox=dict(facecolor='white', alpha=0.6))

Se descargan automáticamente las imágenes desde una carpeta pública en Google Drive 📥 y luego se enumeran todas las rutas de los archivos descargados para poder procesarlos posteriormente 🗂️.

In [ ]:
# Descargando las imagenes de la carpeta
download_drive_folder_public(
    "https://drive.google.com/drive/folders/1CbcVoFIuJg0VZiR26RXPDyObQK14Djci?usp=drive_link",
    "/content/qr"
)

# Enlistando las imagenes descargadas
rutas_imagenes = list_folder_contents("qr")
Retrieving folder contents
Processing file 1I4cOoT-1iQbc1tg8hesw5ARFy-9CeXu4 qrcode1.png
Processing file 1QpxBMNUCGM_6182tWV8YSMFBriOHgT7_ qrcode3.HEIC
Processing file 1pzq1psm095LtAmTbDLX_g8B60PB1Nqkp qrcode4.HEIC
Retrieving folder contents completed
Building directory structure
Building directory structure completed
Downloading...
From: https://drive.google.com/uc?id=1I4cOoT-1iQbc1tg8hesw5ARFy-9CeXu4
To: /content/qr/qrcode1.png
100%|██████████| 27.7k/27.7k [00:00<00:00, 22.2MB/s]
Downloading...
From: https://drive.google.com/uc?id=1QpxBMNUCGM_6182tWV8YSMFBriOHgT7_
To: /content/qr/qrcode3.HEIC
100%|██████████| 2.48M/2.48M [00:00<00:00, 104MB/s]
Downloading...
From: https://drive.google.com/uc?id=1pzq1psm095LtAmTbDLX_g8B60PB1Nqkp
To: /content/qr/qrcode4.HEIC
100%|██████████| 2.18M/2.18M [00:00<00:00, 172MB/s]
Done. Files saved under: /content/qr
Download completed

Se aplica el método de Otsu para binarizar cada imagen y luego se detectan los códigos QR presentes 🔍. Finalmente, se muestran los resultados visualmente y se imprime el contenido encontrado 🖼️📢.

In [ ]:
# Se aplica el flujo de trabajo
for ruta in rutas_imagenes:
    # Aplicar OTSU
    imagen_rgb = iio.imread(ruta)
    imagen_pil = Image.fromarray(imagen_rgb)
    imagen_gris = imagen_pil.convert("L")
    imagen_array = np.array(imagen_gris)
    umbral = threshold_otsu(imagen_array)
    imagen_binaria = (imagen_array > umbral).astype(np.uint8) * 255

    #Extraer QRs sobre imagen binaria (OTSU)
    results = decode_qr_all(imagen_binaria)

    # Desplegar imagenes
    print(f"*"*100)
    print(f"Analizando archivo {ruta}")
    print(f"*"*100)
    plt.figure(figsize=(12,5))
    plt.subplot(1,2,1)
    plt.imshow(imagen_binaria, cmap='gray')
    plt.title('Imagen Binaria (OTSU)')
    plt.axis('off')
    plt.subplot(1,2,2)
    draw_qr_boxes(plt.gca(), imagen_rgb, results)
    plt.title('Detección QR (sobre original)')
    plt.show()

    # Imprimir resultados
    if results:
      print(f"*"*100)
      print(f"CÓDIGOS QR ENCONTRADOS!")
      print(f"*"*100)
      for i, (txt, _) in enumerate(results, 1):
            print(f"  >>>[{i}] {txt}")
    else:
        print(f"{ruta}: No se encontraron códigos QR")

    print("\n" * 3)
****************************************************************************************************
Analizando archivo qr/qrcode1.png
****************************************************************************************************
No description has been provided for this image
****************************************************************************************************
CÓDIGOS QR ENCONTRADOS!
****************************************************************************************************
  >>>[1] https://myqrcode.mobi/fc69d759




****************************************************************************************************
Analizando archivo qr/qrcode3.HEIC
****************************************************************************************************
No description has been provided for this image
****************************************************************************************************
CÓDIGOS QR ENCONTRADOS!
****************************************************************************************************
  >>>[1] OSCAR GARCIA
  >>>[2] LUIS SANCHEZ
  >>>[3] JUAN CARLOS PEREZ
  >>>[4] JAVIER REBULL




****************************************************************************************************
Analizando archivo qr/qrcode4.HEIC
****************************************************************************************************
No description has been provided for this image
****************************************************************************************************
CÓDIGOS QR ENCONTRADOS!
****************************************************************************************************
  >>>[1] PART_ID:178431/DESCRIPTION:FANUC_ROBOT_MOTOR_AXIS2




✅ Reflexión del Mini-Proyecto¶

Se mostró cómo la umbralización con el método de Otsu puede funcionar como un paso clave para la detección y lectura confiable de códigos QR. Otsu permitió separar de forma automática las zonas oscuras y claras de cada imagen, creando una versión binaria más estable para el algoritmo de decodificación. Esto facilitó la lectura tanto en códigos QR limpios, como en escenas con múltiples códigos, e incluso en contextos industriales, donde las condiciones de iluminación y textura pueden variar.

La práctica demostró que el uso de QR tiene una amplia aplicación, desde compartir enlaces y etiquetar objetos personales, hasta la trazabilidad y gestión de componentes industriales. El método de Otsu resulta especialmente útil porque no requiere definir manualmente un umbral, lo que hace posible automatizar el proceso en sistemas reales, móviles o embebidos. En resumen, Otsu fortalece la robustez y simplifica la etapa de preprocesamiento en tareas de reconocimiento visual con códigos QR.

🎯 Conclusiones¶

Esta práctica permitió explorar, aplicar y comparar distintos métodos de umbralización y segmentación sobre imágenes reales, abarcando tanto escenas controladas como contextos visualmente complejos.


🔬 Comprensión de Umbralización Binaria y Multiclase¶

  • Se reforzó el entendimiento del método de Otsu, observando su capacidad para separar imagen en dos clases maximizando la diferencia estadística entre fondo y objeto.
  • Se extendió este criterio con Multi-Otsu, el cual permitió segmentar imágenes en múltiples niveles, mostrando cómo regiones de diferente luminancia pueden ser clasificadas sin intervención manual.
  • Esto permitió comprender que la estructura del histograma es crítica para la efectividad de ambos métodos.

🌁 Aplicación en Imágenes Reales con Variabilidad Lumínica¶

Las imágenes capturadas durante el recorrido en ferry por la Bahía de San Francisco presentaron:

  • Reflexiones intensas en el agua 🌊
  • Neblina o difusión atmosférica 🌫️
  • Zonas arquitectónicas de alto contraste 🌉
  • Elementos de detalle fino (barcos, mástiles, texturas)

Esto permitió observar que:

Condición de la Imagen Efecto Observado
Alto contraste definido Otsu genera segmentaciones estables y limpias
Gradientes suaves o neblina Multi-Otsu identifica rangos intermedios más útiles
Superficies con reflejos Algunos métodos basados en entropía (Li, Yen) producen segmentaciones más equilibradas
Texturas irregulares (agua) Métodos simples como Mean o Triangle tienden a fragmentar resultados

Esto demuestra que la selección del método depende del contenido visual, no solo del nivel de gris global.


🧭 Comparación Sistemática con try_all_threshold¶

La comparación permitió:

  • Evaluar sensibilidad de cada método ante la iluminación.
  • Observar diferencias en términos de agresividad de la segmentación.
  • Detectar casos donde un método falla, por ejemplo:
    • Minimum y Triangle pueden sobsegmentar en escenas con fuerte reflejo.
    • Li y Yen suelen preservar zonas suaves, produciendo separaciones más naturales.
    • Isodata y Otsu funcionan mejor cuando el histograma tiene dos masas diferenciadas.

Esto confirma que no existe un método universalmente óptimo, sino adecuación al contexto.


🧩 Integración con el Mini-Proyecto de QR¶

El mini-proyecto de detección de códigos QR permitió trasladar los conceptos de umbralización a un caso de uso práctico, donde la segmentación binaria es un paso previo esencial para el reconocimiento visual. El método de Otsu demostró ser especialmente útil al producir una binarización automática y consistente, lo cual facilitó la extracción de los patrones modulares característicos del QR. Con ello fue posible detectar y decodificar códigos en tres escenarios distintos: un QR limpio generado digitalmente, una imagen con múltiples QR utilizados para identificación personal, y un código adherido a una pieza en contexto industrial, donde la trazabilidad juega un papel clave.

Este ejercicio reforzó la relevancia de Otsu en aplicaciones donde la separación entre figura y fondo es necesaria para etapas posteriores de análisis, clasificación o decodificación. Además, permitió observar cómo su simplicidad computacional lo hace adecuado para sistemas reales, como cámaras móviles, robots industriales o lectores embarcados, donde se requiere procesar imágenes sin intervención humana constante. En síntesis, el mini-proyecto evidenció que la umbralización binaria no solo es una herramienta teórica, sino un componente central en flujos de visión artificial orientados a identificación, registro y automatización.


Conclusiones Personales¶

🎓 Javier Augusto Rebull Saucedo:
El método de Otsu me resultó especialmente útil porque elimina la necesidad de seleccionar manualmente un umbral. Para mí, la clave es que se basa en una función de optimización clara: minimizar la varianza intra-clase. En escenarios donde se requiere tomar decisiones binarias sobre patrones (por ejemplo, detección de zonas de firma en documentos digitalizados), esta automatización es valiosa siempre que el histograma tenga una separación razonable.

🎓 Juan Carlos Pérez Nava:
La propiedad más importante de Otsu para mí es que no requiere parámetros de entrada, lo que lo hace reproducible. Sin embargo, la práctica me confirma que la eficacia depende fuertemente del contraste entre fondo y región de interés. En imágenes médicas donde hay gradientes suaves o tejidos con intensidades similares, se vuelve necesario combinar Otsu con normalización o ecualización previa.

🎓 Oscar Enrique García García:
Ver Otsu aplicado sobre imágenes naturales refuerza su rol como técnica de segmentación no supervisada basada en estadística básica. Lo considero útil como etapa de preprocesamiento antes de modelos más complejos, especialmente cuando se busca reducir dimensionalidad a regiones relevantes. Sin embargo, es evidente que su desempeño decae cuando la escena no presenta bimodalidad clara, lo cual justifica exploraciones multiclase o basadas en textura.

🎓 Luis Gerardo Sánchez Salazar:
En aplicaciones industriales donde se requiere inspección visual, Otsu es atractivo por ser computacionalmente ligero y determinista. Para detectar bordes de piezas, orificios o zonas de desgaste sobre superficies metálicas, su utilidad es tangible cuando el contraste está bien definido. Pero cuando la iluminación no es controlada o hay reflejos, el método necesita soporte adicional, como iluminación fija o filtros previos, para mantener estabilidad en la segmentación.

✅ Conclusión General¶

En conjunto, esta práctica permitió comprender, comparar y aplicar distintos métodos de umbralización y segmentación en imágenes reales, destacando cómo la forma del histograma, la iluminación y el contenido visual influyen fuertemente en el resultado. Métodos como Otsu y Multi-Otsu resultaron especialmente útiles para separar figura y fondo de manera automática, mientras que la comparación con try_all_threshold demostró que no existe un método único ideal, sino que la elección depende del contexto. Finalmente, la integración con la detección de códigos QR mostró cómo estas técnicas son fundamentales en aplicaciones reales de visión artificial orientadas a identificación y automatización. 🔍🤖✨

📚 Referencias¶

  • Barron, J. T. (2020). A generalization of Otsu’s method and minimum error thresholding. arXiv. Recuperado de https://arxiv.org/pdf/2007.07350

  • Chen, R., Yu, Y., Xu, X., Wang, L., Zhao, H., & Tan, H.-Z. (2019). Adaptive binarization of QR code images for fast automatic sorting in warehouse systems. Sensors, 19(24), 5466. https://doi.org/10.3390/s19245466

  • Chen, R., Li, W., Lan, K., Xiao, J., Wang, L., & Lu, X. (2023). Fast adaptive binarization of QR code images for automatic sorting in logistics systems. Electronics, 12(2), 286. https://doi.org/10.3390/electronics12020286

  • Data Carpentry. (2024). Image processing tutorial: Thresholding. Recuperado de https://datacarpentry.github.io/image-processing/07-thresholding.html

  • GeeksforGeeks. (2025, agosto 18). Python | Thresholding techniques using OpenCV – Set-3 (Otsu thresholding). Recuperado de https://www.geeksforgeeks.org/python/python-thresholding-techniques-using-opencv-set-3-otsu-thresholding/

  • Gonzalez, R. C., & Woods, R. E. (2018). Digital image processing (4.ª ed.). Pearson.

  • Karrach, L., Pivarčiová, E., & Božek, P. (2020). Identification of QR code perspective distortion based on edge directions and edge projections analysis. Journal of Imaging, 6(7), 67. https://pmc.ncbi.nlm.nih.gov/articles/PMC8321072/

  • Murzova, A. (2020, agosto 5). Otsu’s thresholding with OpenCV. LearnOpenCV. Recuperado de https://learnopencv.com/otsu-thresholding-with-opencv/

  • OpenCV. (2025, abril 23). Image thresholding using OpenCV. OpenCV Blog. Recuperado de https://opencv.org/blog/image-thresholding-using-opencv/

  • OpenCV. (s. f.). Image Thresholding (OpenCV-Python Tutorials). Recuperado de https://docs.opencv.org/4.x/d7/d4d/tutorial_py_thresholding.html

  • scikit-image. (2024). Multi-Otsu thresholding. Recuperado de https://scikit-image.org/docs/stable/auto_examples/segmentation/plot_multiotsu.html

  • scikit-image. (2024). Thresholding — example gallery. Recuperado de https://scikit-image.org/docs/stable/auto_examples/segmentation/plot_thresholding.html

  • Tutorialspoint. (s. f.). Multi-Otsu Thresholding (scikit-image). Recuperado de https://www.tutorialspoint.com/scikit-image/scikit-image-multi-otsu-thresholding.htm

  • Wikipedia. (s. f.). Otsu's method. Recuperado de https://en.wikipedia.org/wiki/Otsu%27s_method